﻿using System;
using System.Collections.Generic;
using System.Data.Linq.Mapping;
using System.Linq;
using System.Linq.Expressions;
using System.Text;

namespace Ordinaire.Social.Data
{
    /// <summary>
    /// Class representation of table "Addresses".
    /// </summary>
    [Table]
    public class Addresses : Data<int, string>, ILocation
    {
        #region Variables

        /// <summary>
        /// Unit number.
        /// </summary>
        private string unitNo;

        /// <summary>
        /// Block or street number.
        /// </summary>
        private string blockNo;

        /// <summary>
        /// Street name.
        /// </summary>
        private string streetName;

        /// <summary>
        /// Building name.
        /// </summary>
        private string buildingName;

        /// <summary>
        /// Suburb name.
        /// </summary>
        private string suburb;

        /// <summary>
        /// State name.
        /// </summary>
        private string state;

        /// <summary>
        /// Postal code.
        /// </summary>
        private string postCode;

        /// <summary>
        /// Country.
        /// </summary>
        private string country;

        #endregion

        #region Property methods

        /// <summary>
        /// Gets or sets the entry unique identifier.
        /// </summary>
        [Column(IsPrimaryKey = true, IsDbGenerated = true, UpdateCheck = UpdateCheck.Never)]
        public override int Id
        {
            get { return base.Id; }
            set { base.Id = value; }
        }

        /// <summary>
        /// Gets or sets the unit number.
        /// </summary>
        [Column(DbType = "nvarchar(5)", UpdateCheck = UpdateCheck.Never)]
        public string UnitNo
        {
            get { return this.unitNo; }
            set { this.unitNo = value; }
        }

        /// <summary>
        /// Gets or sets the block number for the specified street name.
        /// </summary>
        [Column(DbType = "nvarchar(5)", UpdateCheck = UpdateCheck.Never)]
        public string BlockNo
        {
            get { return this.blockNo; }
            set { this.blockNo = value; }
        }

        /// <summary>
        /// Gets or sets the street name.
        /// </summary>
        [Column(DbType = "nvarchar(50)", UpdateCheck = UpdateCheck.Never)]
        public string StreetName
        {
            get { return this.streetName; }
            set { this.streetName = value; }
        }

        /// <summary>
        /// Gets or sets the building name, if any.
        /// </summary>
        [Column(DbType = "nvarchar(100)", UpdateCheck = UpdateCheck.Never)]
        public string BuildingName
        {
            get { return this.buildingName; }
            set { this.buildingName = value; }
        }

        /// <summary>
        /// Gets or sets the suburb.
        /// </summary>
        [Column(DbType = "nvarchar(50)", UpdateCheck = UpdateCheck.Never)]
        public string Suburb
        {
            get { return this.suburb; }
            set { this.suburb = value; }
        }

        /// <summary>
        /// Gets or sets the state.
        /// </summary>
        [Column(DbType = "nvarchar(50)", UpdateCheck = UpdateCheck.Never)]
        public string State
        {
            get { return this.state; }
            set { this.state = value; }
        }

        /// <summary>
        /// Gets or sets the postal code.
        /// </summary>
        [Column(DbType = "nvarchar(8)", UpdateCheck = UpdateCheck.Never)]
        public string PostCode
        {
            get { return this.postCode; }
            set { this.postCode = value; }
        }

        /// <summary>
        /// Gets or sets the country.
        /// </summary>
        [Column(DbType = "nvarchar(50)", UpdateCheck = UpdateCheck.Never)]
        public string Country
        {
            get { return this.country; }
            set { this.country = value; }
        }

        /// <summary>
        /// Get or set the username who created this entry.
        /// </summary>
        [Column(DbType = "nvarchar(10) NOT NULL", UpdateCheck = UpdateCheck.Never)]
        public override string CreatedBy
        {
            get { return base.CreatedBy; }
            set { base.CreatedBy = value; }
        }

        /// <summary>
        /// Get or set the date when the entry was first created.
        /// </summary>
        [Column(DbType = "datetime", UpdateCheck = UpdateCheck.Never)]
        public override DateTime? CreateDate
        {
            get { return base.CreateDate; }
            set { base.CreateDate = value; }
        }

        /// <summary>
        /// Get or set the username who last updated this entry.
        /// </summary>
        [Column(DbType = "nvarchar(10)", UpdateCheck = UpdateCheck.Never)]
        public override string UpdatedBy
        {
            get { return base.UpdatedBy; }
            set { base.UpdatedBy = value; }
        }

        /// <summary>
        /// Get or set the date when the entry was last updated.
        /// </summary>
        [Column(DbType = "datetime")]
        public override DateTime? UpdateDate
        {
            get { return base.UpdateDate; }
            set { base.UpdateDate = value; }
        }

        #endregion

        #region Expressions

        /// <summary>
        /// Returns a related expression to find a matching data with the specified address details/parameters.
        /// </summary>
        /// <param name="unitNo">unit number</param>
        /// <param name="blockNo">block/street number</param>
        /// <param name="streetName">street name</param>
        /// <param name="buildingName">building name</param>
        /// <param name="suburb">suburb name</param>
        /// <param name="state">state name</param>
        /// <param name="postCode">postal code</param>
        /// <param name="country">country name</param>
        /// <returns>related expression</returns>
        public static Expression<Func<Addresses, bool>> Matches(
            string unitNo,
            string blockNo,
            string streetName,
            string buildingName,
            string suburb,
            string state,
            string postCode,
            string country
        )
        {
            // Sanitise text before searching
            unitNo       = unitNo.TrimExtraSpaces();
            blockNo      = blockNo.TrimExtraSpaces();
            streetName   = streetName.TrimExtraSpaces();
            buildingName = buildingName.TrimExtraSpaces();
            suburb       = suburb.TrimExtraSpaces();
            state        = state.TrimExtraSpaces();
            postCode     = postCode.TrimExtraSpaces();
            country      = country.TrimExtraSpaces();

            return d =>
                d.UnitNo       == unitNo       &&
                d.BlockNo      == blockNo      &&
                d.StreetName   == streetName   &&
                d.BuildingName == buildingName &&
                d.Suburb       == suburb       &&
                d.State        == state        &&
                d.PostCode     == postCode     &&
                d.Country      == country;
        }

        /// <summary>
        /// Returns a related expression that retrieves required data and transforms them into an instance of Ordinaire.Social.Address class.
        /// </summary>
        /// <returns>related expression</returns>
        public static Expression<Func<Addresses, Address>> GetAddress()
        {
            return d => d.ToAddress();
        }

        #endregion
    }

    /// <summary>
    /// Contains list of extension methods related to Ordinaire.Social.Data.Addresses.
    /// </summary>
    public static class AddressesExtension
    {
        /// <summary>
        /// Converts given data to new instance of Ordinaire.Social.Address.
        /// </summary>
        /// <param name="data">the data to be converted</param>
        /// <returns>new instance of Ordinaire.Social.Address which represents the specified data</returns>
        public static Address ToAddress(this Addresses data)
        {
            if (data == null)
            {
                return null;
            }

            return new Address
            {
                Id           = data.Id,
                UnitNo       = data.UnitNo,
                BlockNo      = data.BlockNo,
                StreetName   = data.StreetName,
                BuildingName = data.BuildingName,
                Suburb       = data.Suburb,
                State        = data.State,
                PostCode     = data.PostCode,
                Country      = data.Country
            };
        }

        /// <summary>
        /// Converts a list of given data to a list of Ordinaire.Social.Address.
        /// </summary>
        /// <param name="data">a collection of data</param>
        /// <returns>a collection of Ordinaire.Social.Address instances</returns>
        public static Dictionary<string, Address> ToAddressList(this Dictionary<string, Addresses> data)
        {
            Dictionary<string, Address> addresses = new Dictionary<string, Address>();
            if (data == null)
            {
                return null;
            }

            foreach (KeyValuePair<string, Addresses> record in data)
            {
                addresses.Add(record.Key, record.Value.ToAddress());
            }

            return addresses;
        }
    }
}
